package com.zhenwei.zscm.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.zhenwei.zscm.common.base.RestMsg;
import com.zhenwei.zscm.common.base.RestResult;
import com.zhenwei.zscm.common.excel.ExcelManager;
import com.zhenwei.zscm.common.excel.model.ShippingExcelModelExcel;
import com.zhenwei.zscm.common.excel.model.ShippingExcelWriteModelExcel;
import com.zhenwei.zscm.common.exception.CustomException;
import com.zhenwei.zscm.common.utils.Linq;
import com.zhenwei.zscm.common.utils.Utils;
import com.zhenwei.zscm.mappings.DictMapping;
import com.zhenwei.zscm.model.dto.dict.DictDTO;
import com.zhenwei.zscm.model.dto.file.FileDTO;
import com.zhenwei.zscm.model.dto.shipping.*;
import com.zhenwei.zscm.model.enums.TemplateEnum;
import com.zhenwei.zscm.model.vo.dict.DictVO;
import com.zhenwei.zscm.model.vo.shipping.ExportVO;
import com.zhenwei.zscm.model.vo.shipping.QueryShippingVO;
import com.zhenwei.zscm.model.vo.shipping.ShippingVO;
import com.zhenwei.zscm.repo.*;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

@AllArgsConstructor
@Service
public class ShippingService {
    private ShippingRepo shippingRepo;
    private ShippingMaterialRepo shippingMaterialRepo;
    private SequencesRepo sequencesRepo;
    private ShippingViewRepo shippingViewRepo;
    private FileRepo fileRepo;
    private DictRepo dictRepo;
    private ShippingExportViewRepo shippingExportViewRepo;
    private  LogRepo logRepo;
    private final String LOG_TYPE = "发货管理";

    @Transactional(rollbackFor = {CustomException.class, Exception.class})
    public RestResult create(ShippingVO vo) {
        // 检查订单编号是否存在
        boolean exists = shippingRepo.checkExists(vo.getOrderNumber());
        if(exists) {
            return RestResult.fail(RestMsg.SHIPPING_ORDER_NUMBER_EXISTS);
        }
        boolean success = shippingRepo.create(vo);
        if (!success){
            return RestResult.fail(RestMsg.FAILURE);
        }
        //
        var created = shippingMaterialRepo.create(vo.getGuid(), vo.getShippingMaterials());
        if (!created){
            throw new CustomException(RestMsg.FAILURE);
        }
        logRepo.createAsync(LOG_TYPE, "添加发货订单->"+vo);
        return RestResult.success(vo.getGuid(), 0);
    }

    @Transactional(rollbackFor = {CustomException.class, Exception.class})
    public RestResult update(ShippingVO vo) {
        ShippingDTO detail = shippingRepo.get(vo.getGuid());
        if (detail == null) {
            return RestResult.fail(RestMsg.SHIPPING_ORDER_NOT_EXISTS);
        }
        boolean updated = shippingRepo.update(vo);
        if (!updated){
            return RestResult.fail(RestMsg.FAILURE);
        }
        // 删除子表数据
        boolean deleted = shippingMaterialRepo.delete(detail.getGuid());
        if (!deleted){
            throw new CustomException(RestMsg.FAILURE);
        }
        // 重新保存
        var created = shippingMaterialRepo.create(detail.getGuid(), vo.getShippingMaterials());
        if (!created){
            throw new CustomException(RestMsg.FAILURE);
        }
        logRepo.createAsync(LOG_TYPE, "修改发货订单->"+vo);
        return RestResult.success();
    }

    public RestResult delete(String orderId){
        ShippingDTO detail = shippingRepo.get(orderId);
        if (detail == null) {
            return RestResult.fail(RestMsg.SHIPPING_ORDER_NOT_EXISTS);
        }
        boolean deleted = shippingRepo.delete(detail.getGuid());
        if (!deleted){
            throw new CustomException(RestMsg.FAILURE);
        }
        deleted = shippingMaterialRepo.delete(detail.getGuid());
        if (!deleted){
            throw new CustomException(RestMsg.FAILURE);
        }
        logRepo.createAsync(LOG_TYPE, "删除发货订单->"+detail.getOrderNumber());
        return RestResult.success();
    }

    public RestResult detail(String orderId){
        ShippingDTO detail = shippingRepo.get(orderId);
        if (detail == null) {
            return RestResult.fail(RestMsg.SHIPPING_ORDER_NOT_EXISTS);
        }
        ShippingDetailDTO result = new ShippingDetailDTO();
        result.setShipping(detail);

        List<ShippingMaterialDTO> materialList = shippingMaterialRepo.list(orderId);
        result.setShippingMaterials(materialList);
        return RestResult.success(result, 0L);
    }
    private String _getOrderNumber() {
        long sequence = sequencesRepo.getShippingOrderSequence();
        return "ZWFH" + DateTime.now().toString("yyyyMMdd") +  StrUtil.padPre(String.valueOf(sequence), 4, "0");
    }
    public RestResult getOrderNumber() {

        var orderNumber = _getOrderNumber();
        logRepo.createAsync(LOG_TYPE, "获取发货订单编号->"+orderNumber);
        return RestResult.success(orderNumber, 0L);
    }

    public RestResult page(QueryShippingVO query) {
        List<PageShippingDTO> page = shippingViewRepo.page(query);
        return RestResult.success(page, query.getTotalCount());
    }

    public RestResult getProductShippingDate(String productSn) {
        var date = shippingViewRepo.getProductShippingDate(productSn);
        return RestResult.success(date, 0);
    }

    @Transactional(rollbackFor = {CustomException.class, Exception.class})
    public RestResult importData(String fileId) {
        FileDTO fileDTO = fileRepo.getOne(fileId);
        if(fileDTO == null) {
            return RestResult.fail(RestMsg.READ_FILE_NOT_EXISTS);
        }
        var excelManager = new ExcelManager<>(ShippingExcelModelExcel.class);
        excelManager.open(fileDTO.getFilePath())
                .readSheet(0)
                .unmergeCell();
        var shippingExcelReadModels = excelManager.getList();
        if(Linq.isEmpty(shippingExcelReadModels)) {
            return RestResult.fail(RestMsg.READ_FILE_IS_EMPTY);
        }
        //
        List<ShippingDTO> shippingList = new ArrayList<>();
        List<ShippingMaterialDTO> shippingMaterialList = new ArrayList<>();

        List<DictDTO> logisticsCompanyList = dictRepo.getList("LOGISTICS_COMPANY");
        List<DictDTO> purchaseCompanyList = dictRepo.getList("PURCHASE_COMPANY");
        List<DictDTO> materialList = dictRepo.getList("MATERIAL");

        shippingExcelReadModels.forEach(shippingExcelReadModel -> {
            String poNumber = shippingExcelReadModel.getPoNumber();
            boolean exists = Linq.exists(shippingList, x -> StrUtil.isNotBlank(x.getPoNumber()) && x.getPoNumber().equals(poNumber));
            if(exists) {
                return;
            }
            // po订单号为空的话，则自己为一个订单，子表物料不做检查
            if(StrUtil.isBlank(poNumber)) {
                // 构造主表数据
                ShippingDTO shippingDTO = resolveShippingDTO(poNumber, shippingExcelReadModel, logisticsCompanyList, purchaseCompanyList);
                // 构造子表
                List<ShippingMaterialDTO> list = resolveShippingMaterialList(shippingDTO.getGuid(), shippingExcelReadModel, materialList);
                shippingList.add(shippingDTO);
                shippingMaterialList.addAll(list);
            } else { // po订单号不为空，需要查询所有相同订单的数据，合并处理
                List<ShippingExcelModelExcel> filterList = Linq.filter(shippingExcelReadModels,
                        x -> x.getPoNumber().equals(poNumber));
                // 构造主表数据
                ShippingDTO shippingDTO = resolveShippingDTO(poNumber, filterList, logisticsCompanyList, purchaseCompanyList);
                // 循环构造所有子表数据
                List<ShippingMaterialDTO> list = resolveShippingMaterialList(shippingDTO.getGuid(), filterList, materialList);
                shippingList.add(shippingDTO);
                shippingMaterialList.addAll(list);
            }
        });
        // 持久化
        boolean saved = shippingRepo.saveAll(shippingList);
        if(!saved) {
            return RestResult.fail(RestMsg.IMPORT_FAILED);
        }
        saved = shippingMaterialRepo.saveAll(shippingMaterialList);
        if (!saved){
            throw new CustomException(RestMsg.IMPORT_FAILED);
        }
        logRepo.createAsync(LOG_TYPE, "导入发货订单数据->"+fileId);
        return RestResult.success();
    }
    private ShippingDTO resolveShippingDTO(String poNumber, List<ShippingExcelModelExcel> models, List<DictDTO> logisticsCompanyList, List<DictDTO> purchaseCompanyList) {
        var dto = new ShippingDTO();
        dto.setGuid(IdUtil.simpleUUID());
        String orderNumber = _getOrderNumber();
        dto.setOrderNumber(orderNumber);
        dto.setPoNumber(poNumber);
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getContactor()));
            dto.setContactor(f == null ? "" : f.getContactor());
        }
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getTelephone()));
            dto.setTelephone(f == null ? "" : f.getTelephone());
        }
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getAddress()));
            dto.setAddress(f == null ? "" : f.getAddress());
        }
        {
            // 物流公司
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getLogisticsCompany()));
            String logisticsCompany = f == null ? "" : Utils.emptyOrSelf(f.getLogisticsCompany());
            resolveLogisticsCompany(logisticsCompanyList, logisticsCompany, dto);
        }
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getLogisticsNumber()));
            dto.setLogisticsNumber(f == null ? "" : f.getLogisticsNumber());
        }
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getPurchaseCompany()));
            // 采购主体
            String purchaseCompany = f == null ? "" : Utils.emptyOrSelf(f.getPurchaseCompany());
            resolvePurchaseCompany(purchaseCompanyList, purchaseCompany, dto);
        }
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getShippingDate()));
            // 出货日期
            String shippingDate = Utils.emptyOrSelf(f == null ? "" : f.getShippingDate());
            dto.setShippingDate(StrUtil.isBlank(shippingDate) ? Utils.DefaultDate() : DateUtil.parse(shippingDate).toJdkDate());
        }
        {
            ShippingExcelModelExcel f = Linq.findFirst(models, x -> StrUtil.isNotBlank(x.getCreateDate()));
            // 订单日期作为这条数据的创建日期
            String createDate = Utils.emptyOrSelf(f == null ? "" : f.getCreateDate());
            dto.setCTime(StrUtil.isBlank(createDate) ? new Date() : DateUtil.parse(createDate).toJdkDate());
        }
        return dto;
    }
    private ShippingDTO resolveShippingDTO(String poNumber, ShippingExcelModelExcel model, List<DictDTO> logisticsCompanyList, List<DictDTO> purchaseCompanyList) {
        var dto = new ShippingDTO();
        dto.setGuid(IdUtil.simpleUUID());
        String orderNumber = _getOrderNumber();
        dto.setOrderNumber(orderNumber);
        dto.setPoNumber(poNumber);
        dto.setContactor(Utils.emptyOrSelf(model.getContactor()));
        dto.setTelephone(Utils.emptyOrSelf(model.getTelephone()));
        dto.setAddress(Utils.emptyOrSelf(model.getAddress()));
        // 物流公司
        String logisticsCompany = Utils.emptyOrSelf(model.getLogisticsCompany());
        resolveLogisticsCompany(logisticsCompanyList, logisticsCompany, dto);
        dto.setLogisticsNumber(Utils.emptyOrSelf(model.getLogisticsNumber()));
        // 采购主体
        String purchaseCompany = Utils.emptyOrSelf(model.getPurchaseCompany());
        resolvePurchaseCompany(purchaseCompanyList, purchaseCompany, dto);
        // 出货日期
        String shippingDate = Utils.emptyOrSelf(model.getShippingDate());
        dto.setShippingDate(StrUtil.isBlank(shippingDate) ? Utils.DefaultDate() : DateUtil.parse(shippingDate).toJdkDate());
        // 订单日期作为这条数据的创建日期
        String createDate = Utils.emptyOrSelf(model.getCreateDate());
        dto.setCTime(StrUtil.isBlank(createDate) ? new Date() : DateUtil.parse(createDate).toJdkDate());
        return dto;
    }
    private List<ShippingMaterialDTO> resolveShippingMaterialList(String orderId, List<ShippingExcelModelExcel> models, List<DictDTO> materialList) {
        List<ShippingMaterialDTO> list = new ArrayList<>();
        // 按物料分组处理
        List<String> materialNames = Linq.distinct(models, ShippingExcelModelExcel::getMaterialName);
        materialNames.forEach(name -> {
            List<ShippingExcelModelExcel> materialGroup = Linq.filter(models, x -> x.getMaterialName().equals(name));
            // 获取这个物料分组里所有的sn
            List<String> snStrList = Linq.select(materialGroup, ShippingExcelModelExcel::getSn);
            List<String> snList= new ArrayList<>();
            snStrList.forEach(sn -> {
                List<String> split = StrUtil.split(sn, "\n", true, true);
                // 去重
                split.forEach(x->{
                    if(Linq.exists(snList, y->y.equals(x))){
                        return;
                    }
                    snList.add(x);
                });
            });
            // 获取这个物料的订单数量总和
            List<Integer> countList = Linq.select(materialGroup, x-> StrUtil.isBlank(x.getCount()) ? 0 :Integer.parseInt(x.getCount()));
            var count = Linq.Sum(countList);

            DictDTO material = resolveMaterial(materialList, name);
            long len = count > snList.size() ? count : snList.size();
            for (int i = 0; i < len; i++) {
                var dto = new ShippingMaterialDTO();
                dto.setGuid(IdUtil.simpleUUID());
                dto.setOrderId(orderId);
                dto.setSn(snList.size() > i ? snList.get(i) : "");
                dto.setCount(1);
                dto.setMaterial(material != null ? material.getName() : "");
                dto.setMaterialCode(material != null ? material.getCode() : "");
                list.add(dto);
            }
        });
        return list;
    }
    private List<ShippingMaterialDTO> resolveShippingMaterialList(String orderId, ShippingExcelModelExcel model, List<DictDTO> materialList) {
        List<ShippingMaterialDTO> list = new ArrayList<>();
        String snStr = model.getSn();
        int count = StrUtil.isBlank(model.getCount()) ? 0 :Integer.parseInt(model.getCount());
        String materialName = Utils.emptyOrSelf(model.getMaterialName());
        if (StrUtil.isBlank(snStr) && StrUtil.isBlank(materialName) && count == 0) {
            return list;
        }
        DictDTO material = resolveMaterial(materialList, materialName);

        int len = count;
        List<String> snList = List.of();
        if (len == 0 && StrUtil.isNotBlank(snStr)) {
            snList = StrUtil.split(snStr, "\n",true,true);
            len = snList.size();
        }
        // 如果sn也没数据，count也是0，并且物料字典也为空，则该数据忽略
        if(len == 0 && material == null) {
            return list;
        }
        for (int i = 0; i < len; i++) {
            var dto = new ShippingMaterialDTO();
            dto.setGuid(IdUtil.simpleUUID());
            dto.setOrderId(orderId);
            dto.setSn(snList.size() > i ? snList.get(i) : "");
            dto.setCount(1);
            dto.setMaterial(material != null ? material.getName() : "");
            dto.setMaterialCode(material != null ? material.getCode() : "");
            list.add(dto);
        }
        return list;
    }
    private DictDTO resolveMaterial(List<DictDTO> materialList, String materialName) {
        if(StrUtil.isBlank(materialName)) {
            return null;
        }
        DictDTO material = Linq.findFirst(materialList, x -> x.getName().equals(materialName));
        if(material == null) {
            // 创建一下这个字典
            var vo = new DictVO();
            vo.setName(materialName);
            vo.setCode(RandomUtil.randomString(6));
            vo.setDictType("MATERIAL");
            if(dictRepo.addDict(vo)){
                material = DictMapping.INSTANCE.vo2Dto(vo);
                materialList.add(material);
            }
        }
        return material;
    }
    private void resolveLogisticsCompany(List<DictDTO> logisticsCompanyList, String logisticsCompany, ShippingDTO dto) {
        if(StrUtil.isBlank(logisticsCompany)) {
            dto.setLogisticsCompany("");
            dto.setLogisticsCompanyCode("");
        } else {
            DictDTO dict = Linq.findFirst(logisticsCompanyList, x -> x.getName().equals(logisticsCompany));
            if(dict != null) {
                dto.setLogisticsCompany(dict.getName());
                dto.setLogisticsCompanyCode(dict.getCode());
            } else {
                // 创建一下这个字典
                var vo = new DictVO();
                vo.setName(logisticsCompany);
                vo.setCode(RandomUtil.randomString(6));
                vo.setDictType("LOGISTICS_COMPANY");
                if(dictRepo.addDict(vo)){
                    dto.setLogisticsCompanyCode(vo.getCode());
                    dto.setLogisticsCompany(vo.getName());
                    logisticsCompanyList.add(DictMapping.INSTANCE.vo2Dto(vo));
                }
            }
        }
    }
    private void resolvePurchaseCompany(List<DictDTO> purchaseCompanyList, String purchaseCompany, ShippingDTO dto) {
        if(StrUtil.isBlank(purchaseCompany)) {
            dto.setPurchaseCompany("");
            dto.setPurchaseCompanyCode("");
        } else {
            DictDTO dict = Linq.findFirst(purchaseCompanyList, x -> x.getName().equals(purchaseCompany));
            if(dict != null) {
                dto.setPurchaseCompany(dict.getName());
                dto.setPurchaseCompanyCode(dict.getCode());
            } else {
                // 创建一下这个字典
                var vo = new DictVO();
                vo.setName(purchaseCompany);
                vo.setCode(RandomUtil.randomString(6));
                vo.setDictType("PURCHASE_COMPANY");
                if(dictRepo.addDict(vo)){
                    dto.setPurchaseCompanyCode(vo.getCode());
                    dto.setPurchaseCompany(vo.getName());
                    purchaseCompanyList.add(DictMapping.INSTANCE.vo2Dto(vo));
                }
            }
        }
    }

    public RestResult export(ExportVO vo) {
        if(Linq.isEmpty(vo.getSelectedIds()) && vo.getQuery() == null) {
            return RestResult.fail();
        }
        List<ShippingExportViewDTO> details;
        // 导出指定数据
        if(Linq.isNotEmpty(vo.getSelectedIds())) {
            details= shippingExportViewRepo.getAll(vo.getSelectedIds());
        } else {
            vo.getQuery().setPageSize(9999999);
            List<PageShippingDTO> page = shippingViewRepo.page(vo.getQuery());
            details = shippingExportViewRepo.getAll(Linq.select(page, PageShippingDTO::getGuid));
        }
        if(Linq.isEmpty(details)) {
            return RestResult.fail(RestMsg.NO_DATA_TO_EXPORT);
        }
        var data = new ArrayList<ShippingExcelWriteModelExcel>();
        details.forEach(d -> {
           var model= new ShippingExcelWriteModelExcel();
           model.setPoNumber(d.getPoNumber());
           model.setTelephone(d.getTelephone());
           model.setAddress(d.getAddress());
           model.setContactor(d.getContactor());
           model.setCount(d.getCount().toString());
           model.setShippingDate(DateUtil.format(d.getShippingDate(), "yyyy-MM-dd"));
           model.setLogisticsCompany(d.getLogisticsCompany());
           model.setLogisticsNumber(d.getLogisticsNumber());
           model.setPurchaseCompany(d.getPurchaseCompany());
           model.setSn(d.getSn());
           model.setCreateDate(DateUtil.format(d.getCTime(), "yyyy-MM-dd HH:mm:ss"));
           model.setMaterialName(d.getMaterial());
           data.add(model);
        });
        String exportFileName = String.format("发货明细导出%s.xlsx", DateUtil.format(new Date(), "yyyy_MM_dd_HH_mm"));
        var excelManage = new ExcelManager<>(ShippingExcelWriteModelExcel.class);
        excelManage
                .useTemplate(TemplateEnum.SHIPPING_EXPORT, exportFileName)
                .writeSheet(data, 0, "发货明细单");
        // 保存文件数据
        var dto = new FileDTO();
        dto.setFileName(excelManage.getFile().getName());
        dto.setFilePath(excelManage.getFile().getAbsolutePath());
        dto.setFileSize(excelManage.getFile().length());
        dto.setFileType(getFileExtension(excelManage.getFile().getName()));
        var saved = fileRepo.Save(dto);
        return RestResult.success(saved ? dto.getGuid() : "", 0);
    }

    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf(".");
        if (lastDotIndex == -1 || lastDotIndex == filename.length() - 1) {
            return "";
        }
        return filename.substring(lastDotIndex);
    }

    public RestResult getRecentlyContactor(String purchaseCompanyCode) {
        ShippingDTO dto = shippingRepo.getRecentlyContactor(purchaseCompanyCode);
        return RestResult.success(new HashMap<String, String>(){
            { put("contactor", dto.getContactor() ); }
            { put("telephone", dto.getTelephone()); }
            { put("address", dto.getAddress()); }
        }, 0);
    }
}
